/**
 * 流程校验工具
 * 用于校验BPMN流程图是否符合规范要求
 */

// 校验结果类型定义
export interface ValidationResult {
  title: string;
  message: string;
  elementId?: string;
}

/**
 * 流程校验器类
 */
export class ProcessValidator {
  private modeler: any;
  private elementRegistry: any;
  private elements: any[];
  private rootElement: any;

  /**
   * 构造函数
   * @param modeler BPMN.js modeler实例
   */
  constructor(modeler: any) {
    this.modeler = modeler;
    this.elementRegistry = modeler.get('elementRegistry');
    this.elements = this.elementRegistry.getAll();
    this.rootElement = this.elements.find((el: any) => el.type === 'bpmn:Process');
  }

  /**
   * 执行所有校验规则
   * @param addMarkers 是否添加标记到问题节点
   * @returns 校验结果数组
   */
  public validateAll(addMarkers: boolean = false): ValidationResult[] {
    const results: ValidationResult[] = [];

    // 合并所有校验结果
    results.push(...this.validateStartEndEvents());
    results.push(...this.validateUserTasks());
    results.push(...this.validateIsolatedNodes());
    results.push(...this.validateSequenceFlow());
    results.push(...this.validateGateways());
    results.push(...this.validateProcessProperties());
    results.push(...this.validateElementNames());

    // 如果需要添加标记，则为所有有问题的节点添加警告样式
    if (addMarkers && results.length > 0) {
      this.addMarkersToProblematicNodes(results);
    }

    return results;
  }
  
  /**
   * 为所有有问题的节点添加警告标记
   * @param results 校验结果数组
   */
  private addMarkersToProblematicNodes(results: ValidationResult[]): void {
    if (!this.modeler) return;
    
    const canvas = this.modeler.get('canvas');
    
    // 先清除所有可能存在的警告样式
    this.clearAllWarningMarkers();
    
    // 为每个有问题的节点添加对应的警告样式
    results.forEach(result => {
      if (result.elementId) {
        canvas.addMarker(result.elementId, `bpmn-warning-node-error`);
      }
    });
  }
  
  /**
   * 清除所有警告标记
   */
  public clearAllWarningMarkers(): void {
    if (!this.modeler) return;
    
    const canvas = this.modeler.get('canvas');
    const elementRegistry = this.modeler.get('elementRegistry');
    
    // 获取所有元素
    const allElements = elementRegistry.getAll();
    
    // 移除所有元素上的警告样式
    allElements.forEach((element: any) => {
      if (element.id) {
        canvas.removeMarker(element.id, 'bpmn-warning-node-error');
        canvas.removeMarker(element.id, 'bpmn-warning-node-warning');
        canvas.removeMarker(element.id, 'bpmn-warning-node-info');
      }
    });
  }

  /**
   * 校验开始和结束节点
   */
  private validateStartEndEvents(): ValidationResult[] {
    const results: ValidationResult[] = [];
    
    // 校验1: 检查是否有开始节点
    const startEvents = this.elements.filter((el: any) => el.type === 'bpmn:StartEvent');
    if (startEvents.length === 0) {
      results.push({
        title: '缺少开始节点',
        message: '流程必须包含至少一个开始节点',
      });
    } else if (startEvents.length > 1) {
      // 检查是否有多个开始节点（可能导致流程混乱）
      results.push({
        title: '多个开始节点',
        message: '流程包含多个开始节点，可能导致流程执行不明确',
      });
    }
    
    // 校验2: 检查是否有结束节点
    const endEvents = this.elements.filter((el: any) => el.type === 'bpmn:EndEvent');
    if (endEvents.length === 0) {
      results.push({
        title: '缺少结束节点',
        message: '流程必须包含至少一个结束节点'
      });
    }
    
    // 校验3: 检查开始节点是否有出口
    startEvents.forEach((startEvent: any) => {
      if (startEvent.outgoing.length === 0) {
        results.push({
          title: '开始节点没有出口',
          message: `开始节点 "${startEvent.businessObject.name || startEvent.id}" 没有连接到任何节点`,
          elementId: startEvent.id
        });
      }
    });
    
    // 校验4: 检查结束节点是否有入口
    endEvents.forEach((endEvent: any) => {
      if (endEvent.incoming.length === 0) {
        results.push({
          title: '结束节点没有入口',
          message: `结束节点 "${endEvent.businessObject.name || endEvent.id}" 没有任何节点连接到它`,
          elementId: endEvent.id
        });
      }
    });
    
    return results;
  }

  /**
   * 校验用户任务
   */
  private validateUserTasks(): ValidationResult[] {
    const results: ValidationResult[] = [];
    
    // 获取所有用户任务
    const userTasks = this.elements.filter((el: any) => el.type === 'bpmn:UserTask');
    
    userTasks.forEach((task: any) => {
      const taskId = task.id;
      const taskName = task.businessObject.name || taskId;
      const assignee = task.businessObject.assignee;
      const candidateUsers = task.businessObject.candidateUsers;
      const candidateGroups = task.businessObject.candidateGroups;
      
      // 校验1: 检查是否配置了审批人
      if (!assignee && (!candidateUsers || candidateUsers.length === 0) && 
          (!candidateGroups || candidateGroups.length === 0)) {
        results.push({
          title: `任务节点未配置审批人`,
          message: `用户任务 "${taskName}" 必须配置审批人、候选用户或候选组`,
          elementId: taskId
        });
      }
      
      // 校验2: 检查任务名称是否为空
      if (!task.businessObject.name || task.businessObject.name.trim() === '') {
        results.push({
          title: '任务节点名称为空',
          message: `用户任务 (ID: ${taskId}) 必须设置名称`,
          elementId: taskId
        });
      }
      
      // 校验3: 检查任务是否有入口和出口
      if (task.incoming.length === 0) {
        results.push({
          title: '任务节点没有入口',
          message: `用户任务 "${taskName}" 没有任何节点连接到它`,
          elementId: taskId
        });
      }
      
      if (task.outgoing.length === 0) {
        results.push({
          title: '任务节点没有出口',
          message: `用户任务 "${taskName}" 没有连接到任何节点`,
          elementId: taskId
        });
      }
    });
    
    return results;
  }

  /**
   * 校验孤立节点
   */
  private validateIsolatedNodes(): ValidationResult[] {
    const results: ValidationResult[] = [];
    
    // 检查流程是否有孤立节点(没有连接的节点)
    this.elements.forEach((el: any) => {
      // 只检查任务、网关等主要节点
      if (['bpmn:Task', 'bpmn:UserTask', 'bpmn:ServiceTask', 'bpmn:Gateway', 'bpmn:SubProcess'].includes(el.type)) {
        const incoming = el.incoming || [];
        const outgoing = el.outgoing || [];
        
        if (incoming.length === 0 && outgoing.length === 0) {
          results.push({
            title: '孤立节点',
            message: `节点 "${el.businessObject.name || el.id}" 没有任何连接，无法在流程中执行`,
            elementId: el.id
          });
        }
      }
    });
    
    return results;
  }

  /**
   * 校验连线
   */
  private validateSequenceFlow(): ValidationResult[] {
    const results: ValidationResult[] = [];
    
    // 获取所有连线
    const sequenceFlows = this.elements.filter((el: any) => el.type === 'bpmn:SequenceFlow');
    
    sequenceFlows.forEach((flow: any) => {
      const flowId = flow.id;
      const sourceId = flow.source?.id;
      const targetId = flow.target?.id;
      
      // 校验1: 检查连线是否有源节点和目标节点
      if (!sourceId || !targetId) {
        results.push({
          title: '连线不完整',
          message: `连线 (ID: ${flowId}) 缺少源节点或目标节点`,
          elementId: flowId
        });
      }
      
      // 校验2: 检查从排他网关出发的连线是否有条件
      if (flow.source && flow.source.type === 'bpmn:ExclusiveGateway' && flow.source.outgoing.length > 1) {
        const conditionExpression = flow.businessObject.conditionExpression;
        if (!conditionExpression) {
          results.push({
            title: '缺少条件表达式',
            message: `从排他网关出发的连线 (ID: ${flowId}) 必须设置条件表达式`,
            elementId: flowId
          });
        }
      }
    });
    
    return results;
  }

  /**
   * 校验网关
   */
  private validateGateways(): ValidationResult[] {
    const results: ValidationResult[] = [];
    
    // 获取所有网关
    const gateways = this.elements.filter((el: any) => 
      ['bpmn:ExclusiveGateway', 'bpmn:ParallelGateway', 'bpmn:InclusiveGateway'].includes(el.type)
    );
    
    gateways.forEach((gateway: any) => {
      const gatewayId = gateway.id;
      const gatewayName = gateway.businessObject.name || gatewayId;
      const gatewayType = gateway.type.replace('bpmn:', '');
      
      // 校验1: 检查网关是否有入口
      if (gateway.incoming.length === 0) {
        results.push({
          title: '网关没有入口',
          message: `${gatewayType} "${gatewayName}" 没有任何节点连接到它`,
          elementId: gatewayId
        });
      }
      
      // 校验2: 检查网关是否有出口
      if (gateway.outgoing.length === 0) {
        results.push({
          title: '网关没有出口',
          message: `${gatewayType} "${gatewayName}" 没有连接到任何节点`,
          elementId: gatewayId
        });
      }
      
      // 校验3: 检查排他网关是否有多个出口
      if (gateway.type === 'bpmn:ExclusiveGateway' && gateway.outgoing.length === 1) {
        results.push({
          title: '排他网关只有一个出口',
          message: `排他网关 "${gatewayName}" 只有一个出口，可能不需要使用网关`,
          elementId: gatewayId
        });
      }
      
      // 校验4: 检查并行网关是否有匹配的合并网关
      if (gateway.type === 'bpmn:ParallelGateway') {
        // 这里只是一个简单的检查，实际上需要更复杂的算法来检查并行网关的匹配
        if (gateway.incoming.length === 1 && gateway.outgoing.length > 1) {
          // 这是一个分支并行网关
          const parallelGateways = this.elements.filter((el: any) => el.type === 'bpmn:ParallelGateway');
          const mergeGateways = parallelGateways.filter((g: any) => g.id !== gatewayId && g.incoming.length > 1 && g.outgoing.length === 1);
          
          if (mergeGateways.length === 0) {
            results.push({
              title: '并行网关缺少匹配的合并网关',
              message: `并行网关 "${gatewayName}" 可能缺少匹配的合并网关`,
              elementId: gatewayId
            });
          }
        }
      }
    });
    
    return results;
  }

  /**
   * 校验流程属性
   */
  private validateProcessProperties(): ValidationResult[] {
    const results: ValidationResult[] = [];
    
    if (this.rootElement) {
      const processId = this.rootElement.businessObject.id;
      const processName = this.rootElement.businessObject.name;
      
      // 校验1: 检查流程ID是否为空
      if (!processId || processId.trim() === '') {
        results.push({
          title: '流程ID为空',
          message: '流程必须设置ID'
        });
      }
      
      // 校验2: 检查流程名称是否为空
      if (!processName || processName.trim() === '') {
        results.push({
          title: '流程名称为空',
          message: '流程必须设置名称'
        });
      }
    } else {
      results.push({
        title: '流程根元素缺失',
        message: '无法找到流程根元素'
      });
    }
    
    return results;
  }

  /**
   * 校验元素名称
   */
  private validateElementNames(): ValidationResult[] {
    const results: ValidationResult[] = [];
    
    // 检查所有主要元素是否有名称
    this.elements.forEach((el: any) => {
      if (['bpmn:Task', 'bpmn:UserTask', 'bpmn:ServiceTask', 'bpmn:Gateway', 'bpmn:StartEvent', 'bpmn:EndEvent'].includes(el.type)) {
        const elementId = el.id;
        const elementName = el.businessObject.name;
        const elementType = el.type.replace('bpmn:', '');
        
        if (!elementName || elementName.trim() === '') {
          results.push({
            title: `${elementType}名称为空`,
            message: `${elementType} (ID: ${elementId}) 应该设置名称以提高可读性`,
            elementId: elementId
          });
        }
      }
    });
    
    return results;
  }

  /**
   * 定位到指定元素
   * @param elementId 元素ID
   */
  public locateElement(elementId: string): void {
    if (!this.modeler || !elementId) return;
    
    try {
      // 获取元素
      const element = this.elementRegistry.get(elementId);
      
      if (element) {
        // 获取画布
        const canvas = this.modeler.get('canvas');
        
        // 选中元素
        const selection = this.modeler.get('selection');
        selection.select(element);
        
        // 将视图中心设置到元素位置
        canvas.scrollToElement(element);
        
        // 移除之前可能存在的警告样式
        canvas.removeMarker(elementId, 'bpmn-warning-node-error');
        
        // 根据严重程度添加不同的警告样式
        canvas.addMarker(elementId, `bpmn-warning-node-error`);
        
        // 5秒后移除警告样式
        setTimeout(() => {
          canvas.removeMarker(elementId, `bpmn-warning-node-error`);
        }, 5000);
      }
    } catch (err) {
      console.error('定位元素失败', err);
    }
  }
}

/**
 * 创建流程校验器实例
 * @param modeler BPMN.js modeler实例
 * @returns 流程校验器实例
 */
export function createProcessValidator(modeler: any): ProcessValidator {
  return new ProcessValidator(modeler);
}